/**
 * @class 实体父类
 * @author DuskyHuang 圣子
 * @description 被系统管理的实体（E）
*/

import { _decorator, Component, Node, Vec3, IVec3Like } from 'cc';
import { notifier, entities } from '../boot';
const { ccclass } = _decorator;

@ccclass('cfw.Entity')
export default abstract class Entity extends Component {

    protected __preload(): void {
        if (isFunc(super.__preload)) super.__preload();
        entities.unshiftUnique(this);
    }

    protected on(type: any, handler: Handler, priority: number = 0): void {
        notifier.attach(type, handler, this, false, priority);
    }

    protected once(type: any, handler: Handler, priority: number = 0): void {
        notifier.attach(type, handler, this, true, priority);
    }

    protected off(type: any, handler: Handler): void {
        notifier.detach(type, handler, this);
    }

    protected notify(type: any, ...args: any[]): void {
        notifier.dispatch(type, ...args);
    }

    protected onDestroy(): void {
        notifier.unload(this);
        entities.remove(this);
    }

    /** 本地x坐标 */
    public get x(): num.px { return this.node.position.x; }
    public set x(arg: num.px) { const { y, z } = this.node.position; this.node.setPosition(arg, y, z); }

    /** 本地y坐标 */
    public get y(): num.px { return this.node.position.y; }
    public set y(arg: num.px) { const { x, z } = this.node.position; this.node.setPosition(x, arg, z); }

    /** 本地z坐标 */
    public get z(): num.px { return this.node.position.z; }
    public set z(arg: num.px) { const { x, y } = this.node.position; this.node.setPosition(x, y, arg); }

    /** 世界x坐标 */
    public get wx(): num.px { return this.node.worldPosition.x; }
    public set wx(arg: num.px) { const { y, z } = this.node.worldPosition; this.node.setWorldPosition(arg, y, z); }

    /** 世界y坐标 */
    public get wy(): num.px { return this.node.worldPosition.y; }
    public set wy(arg: num.px) { const { x, z } = this.node.worldPosition; this.node.setWorldPosition(x, arg, z); }

    /** 世界z坐标 */
    public get wz(): num.px { return this.node.worldPosition.z; }
    public set wz(arg: num.px) { const { x, y } = this.node.worldPosition; this.node.setWorldPosition(x, y, arg); }

    /** 实体节点是否激活 */
    public get activated(): boolean { return this.node?.active; }
    public set activated(arg: boolean) { this.node.active = arg; }

    /** 
     * 变换实体到另一个节点下
     * @param offset 并产生平移，默认不偏移
     */
    public traverse(parent: Node, offset?: IVec3Like): void {
        if (parent instanceof Node && this.node.parent !== parent) {
            const pos = Vec3.copy(v3a, offset ? offset : Vec3.ZERO);
            this.node.setWorldPosition(this.node.getWorldPosition(pos));
            this.node.parent = parent;
        }
    }

    /** 
     * 将实体面向
     * @param target 面向另一个实体
     * @param to 面向某节点
     * @param point 面向位置点
     * @param x 面向坐标x
     * @param y 面向坐标y
     * @param z 面向坐标z
     * @explain 实体-Z方向为面向
     */
    public face(target: Entity): void;
    public face(to: Node): void;
    public face(point: IVec3Like): void;
    public face(x: num.px, y: num.px, z: num.px): void;
    public face(arg: Entity | Node | IVec3Like | num.px, y?: num.px, z?: num.px): void {
        Entity.face(this, arg as any, y, z);
    }

    /** 
     * 实体到目标距离
     * @param target 目标实体
     * @param to 目标节点
     * @param point 目标位置点
     * @param x 目标坐标x
     * @param y 目标坐标y
     * @param z 目标坐标z
     * @return 距离值
     */
    public distance(target: Entity): num.px;
    public distance(to: Node): num.px;
    public distance(point: IVec3Like): num.px;
    public distance(x: num.px, y: num.px, z: num.px): num.px;
    public distance(arg: Entity | Node | IVec3Like | num.px, y?: num.px, z?: num.px): num.px {
        return Entity.distance(this, arg as any, y, z);
    }

    public static face(from: Entity, target: Entity): void;
    public static face(from: Entity, to: Node): void;
    public static face(from: Entity, point: IVec3Like): void;
    public static face(from: Entity, x: num.px, y: num.px, z: num.px): void;
    public static face(from: Entity, arg: Entity | Node | IVec3Like | num.px, y?: num.px, z?: num.px): void {
        Vec3.copy(v3a, arg instanceof Entity ? arg.node.worldPosition : arg instanceof Node ?
            arg.worldPosition : (isObject(arg) && 'x' in arg) ? arg : v3a.set(arg, y, z));
        Vec3.subtract(v3a, v3a, from.node.worldPosition);
        from.node.forward = v3a;
    }

    public static distance(from: Entity, target: Entity): num.px;
    public static distance(from: Entity, to: Node): num.px;
    public static distance(from: Entity, point: IVec3Like): num.px;
    public static distance(from: Entity, x: num.px, y: num.px, z: num.px): num.px;
    public static distance(from: Entity, arg: Entity | Node | IVec3Like | num.px, y?: num.px, z?: num.px): num.px {
        Vec3.copy(v3a, arg instanceof Entity ? arg.node.worldPosition : arg instanceof Node ?
            arg.worldPosition : (isObject(arg) && 'x' in arg) ? arg : v3a.set(arg, y, z));
        return Vec3.distance(from.node.worldPosition, v3a);
    }
}

const v3a = new Vec3;